using System;
using System.Collections.Generic;
using System.Collections ;
using System.Text;
using CommonTypes.Events;
using CommonTypes;

namespace MVCFramework.Models
{
    /// <summary>
    /// Used for Counting of the modelData mostly the implicit collection data .
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable ]
    public class IdManager<T> where T :class , ICountable 
    {
        # region " private data Decleration"

        private Hashtable Objects;

        private Stack<int> freeIDs;
        public Stack<int> FreeIDs
        {
            get { return freeIDs; }
            set { freeIDs = value; }
        }

        private List <int> globalIDs;
        private  List<int> GlobalIDs
		{
			set {
				globalIDs = value;
			}
			
			get {
				return globalIDs;
			}
		}

        private IDictionary<int, T > idMapToObject;
        private IDictionary<int, T > IdMapToObject
        {
            get { return idMapToObject; }
            set { idMapToObject = value; }
        }

        # endregion

        public  IdManager()
        {
            freeIDs = new Stack <int>();
            Objects = new Hashtable();
            globalIDs = new List<int>();
            IdMapToObject = new Dictionary<int, T >();
        }

        public void OnCreateHandler(object sender , CreationEventArgs e)
        {
            if (sender is T)
                Register(sender as T);
        }
        
        # region "Public  Decleration "

        private int maxId;
        public int MaxId
        {
            set
            {
                maxId = value;
            }
            get
            {
                return maxId;
            }

        }

        public int Register(T _object)
        {
            if (IdMapToObject.Values.Contains(_object))
            {
                return -1;
            }
            int newid;
            newid = MakeNewRegistration(_object);
            _object.Id = newid;
            return newid;
        }

        public void Remove(T _object)
        {

            if (IdMapToObject.ContainsKey(_object .Id))
            {
                int id = _object.Id;
                IdMapToObject.Remove(id);
                FreeIDs.Push(id);
            }
        }

        public void Remove(int id)
        {
            if (IdMapToObject.ContainsKey(id))
            {
                IdMapToObject.Remove(id);
                FreeIDs.Push(id);
            }
        }
        public object GetObjectById(int id)
        {
            if (this.IdMapToObject.ContainsKey(id))
                return IdMapToObject[id];
            return null;

        }

        #endregion

        # region " Help functions"

        private  List<int> getObject(string type)
        {
            if (Objects.Contains(type))
            {
                return (List<int>)Objects[type];
            }
            return null;
        }

        private void setID(string type, int id)
        {
            List<int> ElementIDList;

            if (!globalIDs.Contains(id))
            {
                if (Objects.Contains(type))
                {
                    ElementIDList = (List<int>)Objects[type];
                    ElementIDList.Add(id);
                    globalIDs.Add(id);
                    return;
                }

                ElementIDList = new List<int>();
                ElementIDList.Add(id);
                globalIDs.Add(id);
                Objects.Add(type, ElementIDList);
            }
        }

        private List<int> getType(string type)
        {
            if (Objects.Contains(type))
                return (List<int>)Objects[type];
            return null;
        }

        private int getNewID()
        {
            int nextFreeId;
            IDOrderStrategy orderAlgorithm;
            if (FreeIDs.Count > 0)
            {
                int freeid = FreeIDs.Pop();
                return freeid;
            }
            orderAlgorithm = new LIFOStrategy(globalIDs);
            nextFreeId = orderAlgorithm.getID();
            MaxId = nextFreeId;
            return nextFreeId;

        }

        private int MakeNewRegistration(T _object)
        {
            int id = getNewID();
            AddToHashTable(id, _object);
            setID(_object.GetType().Name, id);
            return id;
        }

        private bool AddToHashTable(int id , T _object)
        {
            IdMapToObject.Add(id, _object); return true;
        }

        # endregion

        public void FillIdManager(IEnumerable objects)
        {
            foreach (object _object in objects)
                if (_object is T)
                    AddToHashTable(((T)_object).Id, (T)_object);
        }
    }

    # region " new IDs generation Algorithms " 
    abstract public class IDOrderStrategy
    {
        protected int freeid ;
        abstract public int getID();
    }
    public class LIFOStrategy : IDOrderStrategy
    {
        // find the min id that it is not in use
        List<int> GlobalIDs;
        public LIFOStrategy(List<int> GlobalIDs)
        {
            this.GlobalIDs = GlobalIDs;

        }
        public override int getID()
        { 
            int newid ;
            if (GlobalIDs.Count > 0)
                newid = GlobalIDs[GlobalIDs.Count - 1] + 1;
            else newid = 1;
            

            return newid;
        }
    }
    # endregion 

    /*
    public class ModelIDManager : IdManager<ICountable>
    {
        private static ModelIDManager instance;
        public static ModelIDManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new ModelIDManager();
                return instance;

            }
        }
        
    }
     * */

}
